'''
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with self work for additional information
 * regarding copyright ownership.  The ASF licenses self file
 * to you under the Apache License, Version 2.0 (the
 * "License") you may not use self file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http:#www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
'''
from base.datatypes.BaseDatatype import BaseDatatype
from base.datatypes.xsd.XSDDatatype import XSDDatatype
from base.datatypes.xsd.impl import XMLLiteralType
from base.datatypes.xsd.impl.RDFLangString import RDFLangString
from base.java.HashMap import HashMap
from base.shared import JenaParameters
from base.shared.DatatypeFormatException import DatatypeFormatException
from base.util import IteratorUtil

#import java.net.URI;
#import java.net.URL;
#import java.util.Iterator;
#import java.util.concurrent.ConcurrentHashMap;

#import org.apache.jena.datatypes.xsd.XSDDatatype;
#import org.apache.jena.datatypes.xsd.impl.RDFLangString;
#import org.apache.jena.datatypes.xsd.impl.XMLLiteralType;
#import org.apache.jena.shared.impl.JenaParameters;

'''
 * The TypeMapper provides a global registry of known datatypes.
 * The datatypes can be retrieved by their URI or from the java class
 * that is used to represent them.
'''

# Added extended set of class mappings and getTypeByClass
# as suggested by Thorsten Moeller. der 8/5/09

theTypeMap = None

class TypeMapper:

#=======================================================================
# Statics

    '''
     * Return the single global instance of the TypeMapper.
     * Done self way rather than simply making the static
     * field directly accessible to allow us to dynamically
     * replace the entire mapper table if needed.
    '''
    # param 
    # result static TypeMapper

    def __init__(self):
        self.reset()
        self.uriToDT = HashMap()
        self.classToDT = HashMap()

    # param TypeMapper typeMapper
    # result static void
    def setInstance(self, typeMapper):
        theTypeMap = typeMapper
    

    '''
     * The single global instance of the TypeMapper
    '''
    # private static TypeMapper theTypeMap

    '''
     * Static initializer. Adds builtin datatypes to the mapper.
    '''
    # static:
    #     reset()
    

    # param 
    # result static void
    def reset(self):
        theTypeMap = TypeMapper()
        # theTypeMap.registerDatatype(XMLLiteralType.theXMLLiteralType)
        # theTypeMap.registerDatatype(RDFLangString.rdfLangString)
        # XSDDatatype.loadXSDSimpleTypes(theTypeMap)

        # add primitive types 在java 中这里是 原生类型 对应 put 封装类型指向的RDFDataType
        # theTypeMap.classToDT.put(float.__class__, theTypeMap.classToDT.get(float.__class__))
        # theTypeMap.classToDT.put(double.__class__, theTypeMap.classToDT.get(Double.__class__))
        # theTypeMap.classToDT.put(int.__class__, theTypeMap.classToDT.get(int.__class__))
        # theTypeMap.classToDT.put(long.__class__, theTypeMap.classToDT.get(Long.__class__))
        # theTypeMap.classToDT.put(short.__class__, theTypeMap.classToDT.get(Short.__class__))
        # theTypeMap.classToDT.put(byte.__class__, theTypeMap.classToDT.get(Byte.__class__))
        # theTypeMap.classToDT.put(boolean.__class__, theTypeMap.classToDT.get(Boolean.__class__))

        # add missing character types
        #theTypeMap.classToDT.put(char.__class__, theTypeMap.classToDT.get(String.__class__))
        #theTypeMap.classToDT.put(Character.__class__, theTypeMap.classToDT.get(String.__class__))

        # add mapping for URL class
        # theTypeMap.classToDT.put(URL.__class__, theTypeMap.classToDT.get(URI.__class__))
    

    # public TypeMapper():
    #     super().__init__()
    

#=======================================================================
# Variables

    '''
     * Map from uri to datatype
    '''
    # param 
    # result final ConcurrentHashMap<String, RDFDatatype> uriToDT = new
    # def ConcurrentHashMap<>(self)

    '''
     * Map from java class to datatype
    '''
    # param 
    # result final ConcurrentHashMap<Class<?>, RDFDatatype> classToDT = new
    # def ConcurrentHashMap<>(self)

#=======================================================================
# Methods


    '''
     * Version of getTypeByName which will treat unknown URIs as typed literals but with
     * just the default implementation
     * <p>
     * RDF 1.1: null for:@code urireturns null and it will mean:@code xsd:string
     * because plain literals (no lang tag) and xsd:strings are now the same.
     *
     * @param uri the URI of the desired datatype
     * @return Datatype the datatype definition registered at uri, if there is no such
     * registered type it returns a instance of the default datatype
     * implementation, if the uri is null it returns null (indicating a plain RDF
     * literal).
    '''
    # param final String uri
    # result RDFDatatype
    def getSafeTypeByName(self, uri):
        if (uri is None):
            # Plain literal
            return None
        
        #RDFDatatype
        dtype = self.uriToDT.get(uri)
        if (dtype is None):
            # Unknown datatype
            if (JenaParameters.enableSilentAcceptanceOfUnknownDatatypes):
                dtype = BaseDatatype(uri)
                self.registerDatatype(dtype)
            else:
                raise DatatypeFormatException("Attempted to created typed literal using an unknown datatype - " + uri)
            
        
        return dtype
    

    '''
     * Lookup a known datatype. An unknown datatype or a datatype with uri null
     * will return null will mean that the value will be treated as a old-style plain literal.
     *
     * @param uri the URI of the desired datatype
     * @return Datatype the datatype definition of null if not known.
    '''
    # param final String uri
    # result RDFDatatype
    def getTypeByName(self, uri):
        return None if uri is None else self.uriToDT.get(uri)
    

    '''
     * Method getTypeByValue. Look up a datatype suitable for representing
     * the given java value object.
     *
     * @param value a value instance to be represented
     * @return Datatype a datatype whose value space matches the java class
     * of <code>value</code>
    '''
    # param final Object value
    # result RDFDatatype
    def getTypeByValue(self, value):
        return self.classToDT.get(value.getClass())
    

    '''
     * List all the known datatypes
    '''
    # param 
    # result Iterator<RDFDatatype>
    def listTypes(self):
        #TODO 这里的iterator 怎么处理
        return IteratorUtil.iterator(self.uriToDT.values())
    

    '''
     * Look up a datatype suitable for representing instances of the
     * given Java class.
     *
     * @param clazz a Java class to be represented
     * @return a datatype whose value space matches the given java class
    '''
    # param final Class<?> clazz
    # result RDFDatatype
    def getTypeByClass(self, clazz):
        return None if clazz is None else self.classToDT.get(clazz)
    

    '''
     * Register a datatype
    '''
    # param final RDFDatatype type
    # result void
    def registerDatatype(self, dataType):
        self.uriToDT.put(dataType.getURI(), dataType)
        #final Class<?>
        jc = dataType.getJavaClass()
        if (jc is not None):
            self.classToDT.put(jc, dataType)
        
    

    '''
     * Remove a datatype registration.
    '''
    # param final RDFDatatype type
    # result void
    def unregisterDatatype(self, type):
        self.uriToDT.remove(type.getURI())
        #final Class<?>
        jc = type.getJavaClass()
        if (jc is not None):
            self.classToDT.remove(jc)
        
def getInstance():
    return theTypeMap